﻿using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Abp.Application.Services;
using Abp.Domain.Repositories;
using Abp.UI;
using AVISOutBound.SSOServer.Orders;
using AVISOutBound.SSOServer.Orders.AbgOrderPayLogs;
using AVISOutBound.SSOServer.Payment.Alipay;
using AVISOutBound.SSOServer.Payment.Dto;
using AVISOutBound.SSOServer.Payment.Wechat;
using IdentityModel.Client;
using Microsoft.AspNetCore.Http;

namespace AVISOutBound.SSOServer.Payment
{
    /// <summary>
    /// 支付单服务
    /// 
    /// NET Core 2.0 使用支付宝
    /// 参考网址：https://www.cnblogs.com/Leo_wl/p/7615487.html
    /// </summary>
    public class PayOrderAppService : ApplicationService, IPayOrderAppService
    {
        private IRepository<AbgOrderPayLog, long> _payOrderRepository;
        private IOrderRepository _orderRepository;

        private IOrderManager _orderManager;
        private IAlipayManager _alipayManager;
        private IWechatManager _wechatManager;

        /// <summary>
        /// 支付回调地址
        /// </summary>
        private const string CALLBACK_URL = "http://sso.ningruisoft.com/api/payOrder/alipaynotify";

        public PayOrderAppService(
            IRepository<AbgOrderPayLog, long> payOrderRepository
            , IOrderRepository orderRepository
            , IOrderManager orderManager
            , IAlipayManager alipayManager
            , IWechatManager wechatManager)
        {
            _payOrderRepository = payOrderRepository;
            _orderRepository = orderRepository;

            _orderManager = orderManager;
            _alipayManager = alipayManager;
            _wechatManager = wechatManager;
        }

        /// <summary>
        /// 创建测试支付单。用于测试
        /// </summary>
        /// <param name="input">输入参数</param>
        /// <returns></returns>
        public async Task<PayOrderOutput> TestCreate(CreatePayOrderInput input)
        {
            var amount = Convert.ToDecimal(0.01);
            string codeUrl = string.Empty;
            // TODO 支付订单过期时间（分钟） ，需要重构到一个方法
            double timeExpire = 5; // 默认是5分钟

            if (input.PayType == PayType.Alipay)
            {
                var payOrderNo = $"{input.OrderNo}_P{DateTime.Now:yyyyMMddHHmmss}";
                Logger.Info($"支付宝：发起支付订单（{payOrderNo}），金额是：{amount.ToString("F")}，有效时间（分钟）为：{timeExpire}");
                codeUrl = await Alipay(input, amount.ToString("F"), payOrderNo, timeExpire, input.OrderNo);

                // TODO 需要会写支付链接地址
            }
            else if (input.PayType == PayType.Wechat)
            {
                var payOrderNo = $"{input.OrderNo}_P{DateTime.Now:yyyyMMddHHmmss}";
                Logger.Info($"微信支付：发起支付订单（{payOrderNo}），金额是：{amount.ToString("F")}，有效时间（分钟）为：{timeExpire}");
                codeUrl = await WechatPay(input, amount.ToString("F"), payOrderNo, timeExpire, input.OrderNo);
                //throw new UserFriendlyException("暂不支持微信支付");
            }

            PayOrderOutput result = new PayOrderOutput();
            result.OrderNo = input.OrderNo;
            result.PayType = input.PayType;
            result.Code = codeUrl;
            result.Amount = amount;
            result.IsMobilePay = input.IsMobilePay;
            result.CreateTime = DateTime.Now;
            return result;
        }

        /// <summary>
        /// 创建支付单
        /// </summary>
        /// <param name="input">输入参数</param>
        /// <returns></returns>
        public async Task<PayOrderOutput> Create(CreatePayOrderInput input)
        {
            // TODO  1. 创建支付单的时候，需要采用orderId 加 orderNo，确保订单信息正确
            //          通过orderId 获取订单信息，然后在业务逻辑中判断 orderNo 是否相等
            //       2. 订单信息需要跟客户（张珉敏）讨论，是否可以直接读取数据库的信息，不通过Wizard接口。目前是通过订单详情这个公共的方法的。
            var order = _orderManager.GetDBOrder(input.OrderNo);

            if (order == null)
            {
                throw new UserFriendlyException("订单不存在");
            }

            if (order.OrderStatus == (int)OrderStatus.Cancel)
            {
                throw new UserFriendlyException("订单已取消");
            }

            if (order.OrderStatus.HasValue && order.OrderStatus != (int)OrderStatus.Normal)
            {
                throw new UserFriendlyException("订单已支付");
            }

            var amount = Convert.ToDecimal(order.RMBAmount);
            string codeUrl = string.Empty;

            // TODO 支付订单过期时间（分钟） ，需要重构到一个方法
            double timeExpire = 5; // 默认是5分钟
            if (order.PayTimeout.HasValue)
            {
                TimeSpan spanExpire = order.PayTimeout.Value - Abp.Timing.Clock.Now;

                // 四舍六入五取偶。说明：对于1.5，因要返回偶数，所以结果为2。
                //timeExpire = Math.Round(spanExpire.TotalMinutes);

                // 总是舍去小数
                timeExpire = Math.Floor(spanExpire.TotalMinutes);
                //timeExpire = timeExpire > 5 ? 5 : timeExpire; // 客户说不用过期
                if (timeExpire <= 0)
                {
                    throw new UserFriendlyException("订单已关闭（过期后自动关闭），不能发起支付了。");
                }
            }
            else
            {
                Logger.Warn($"订单号（{order.OrderNo}）发起了在线支付，订单支付过期时间为空，订单类型为：{order.OrderType}，订单状态为：{order.OrderStatus}");
                throw new UserFriendlyException("只有在线预订的租车订单才可以发起支付");
            }

            // TODO 需要判断是否已经发起过支付订单，如果支付订单创建时间不超过1分钟，则直接返回上一个支付订单的地址。

            if (input.PayType == PayType.Alipay)
            {
                var payOrderNo = $"{order.OrderNo}_P{DateTime.Now:yyyyMMddHHmmss}";
                CreatePayOrder(order.Id, order.OrderNo, payOrderNo, Convert.ToSingle(amount), input.PayType);
                //CreatePayOrder(DateTime.Now.ToString("yyyyMMddHHmmss"), payOrderNo, Convert.ToSingle(0.01), input.PayType);
                Logger.Info($"支付宝：发起支付订单（{payOrderNo}），金额是：{amount.ToString("F")}，有效时间（分钟）为：{timeExpire}");
                codeUrl = await Alipay(input, amount.ToString("F"), payOrderNo, timeExpire, order.OrderNo);

                // TODO 需要会写支付链接地址
            }
            else if (input.PayType == PayType.Wechat)
            {
                var payOrderNo = $"{order.OrderNo}_P{DateTime.Now:yyyyMMddHHmmss}";
                CreatePayOrder(order.Id, order.OrderNo, payOrderNo, Convert.ToSingle(amount), input.PayType);
                Logger.Info($"微信支付：发起支付订单（{payOrderNo}），金额是：{amount.ToString("F")}，有效时间（分钟）为：{timeExpire}");
                codeUrl = await WechatPay(input, amount.ToString("F"), payOrderNo, timeExpire, order.OrderNo);
                //throw new UserFriendlyException("暂不支持微信支付");
            }

            PayOrderOutput result = new PayOrderOutput();
            result.OrderNo = input.OrderNo;
            result.PayType = input.PayType;
            result.Code = codeUrl;
            result.Amount = amount;
            result.IsMobilePay = input.IsMobilePay;
            result.CreateTime = DateTime.Now;
            return result;
        }

        /// <summary>
        /// 获取订单状态。轮询订单状态-判断是否支付成功
        /// </summary>
        /// <param name="orderNo">订单号</param>
        /// <returns></returns>
        public async Task<int> GetOrderStatus(string orderNo)
        {
            var order = _orderManager.GetDBOrder(orderNo);
            if (!order.OrderStatus.HasValue)
            {
                return 0;
            }
            else
            {
                return order.OrderStatus.Value;
            }
        }

        /// <summary>
        /// 创建支付单
        /// </summary>
        /// <param name="orderId">订单ID</param>
        /// <param name="orderNo">订单号</param>
        /// <param name="payOrderNo">支付订单号</param>
        /// <param name="amount">金额</param>
        /// <param name="payType">支付类型</param>
        private void CreatePayOrder(int orderId, string orderNo, string payOrderNo, float amount, PayType payType)
        {
            AbgOrderPayLog entity = new AbgOrderPayLog()
            {
                Amount = amount,
                OrderId = orderId,
                OrderNo = orderNo,
                PayOrderNo = payOrderNo,
                PayType = (int)payType,
                Status = (int)PayStatus.Normal
            };

            this._payOrderRepository.Insert(entity);
        }

        /// <summary>
        /// 支付宝支付
        /// </summary>
        /// <param name="input">输入参数</param>
        /// <param name="amount">订单金额</param>
        /// <param name="orderNo">支付订单号</param>
        /// <returns></returns>
        private async Task<string> Alipay(CreatePayOrderInput input, string amount, string orderNo, double timeExpire, string avisOrderNo)
        {
            string result;
            var timeoutExpress = $"{timeExpire}m";
            if (input.IsMobilePay)
            {
                result = await this._alipayManager.WapPay("Avis国际租车订单-" + avisOrderNo, $"Avis国际租车订单，业务订单号：{avisOrderNo}", amount, orderNo
                    , input.RedirectUrl, CALLBACK_URL, timeoutExpress);
            }
            else
            {
                result = await this._alipayManager.PcPay("Avis国际租车订单-" + avisOrderNo, $"Avis国际租车订单，业务订单号：{avisOrderNo}", amount, orderNo
                    , input.RedirectUrl, CALLBACK_URL, timeoutExpress);
            }

            return "https://openapi.alipay.com/gateway.do?" + result;
        }

        /// <summary>
        /// 微信支付
        /// </summary>
        /// <param name="input">输入参数</param>
        /// <param name="amount">订单金额</param>
        /// <param name="orderNo">支付订单号</param>
        /// <param name="timeExpire">订单超时时间</param>
        /// <param name="avisOrderNo">订单号</param>
        /// <returns></returns>
        private async Task<string> WechatPay(CreatePayOrderInput input, string amount, string orderNo,
            double timeExpire, string avisOrderNo)
        {
            string result=string.Empty;
            var payParam=new WechatPayParam()
            {
                AppId = "wxc4e32ab689f1502f",
                MchId  = "1503405211",
                Key = "sgIWFCxxphnMbRD6fxXILn4BpdMXMfby",
                DeviceInfo = "WEB",
                ServiceIpAddress = "101.132.35.231",
                Domain = "http://sso.ningruisoft.com",
                NotifyUrl = "/api/payOrder/wechatpaynotify",
                Body = $"Avis国际租车订单，业务订单号：{avisOrderNo}",
                OutTradeNo = orderNo,
                TotalFee = decimal.Parse(amount),
                StartTime = DateTime.Now,
                ExpireTime = DateTime.Now.AddMinutes(timeExpire)
            };

            // 微信公众号支付-需要配置支付授权域名。需要传递openid
            if (input.IsMobilePay && input.IsWechatBrowser)
            {
                result = (await this._wechatManager.PublicPay(payParam)).PrepayId;
            }

            // 微信Wap支付-需要配置H5支付授权域名
            if (input.IsMobilePay && !input.IsWechatBrowser)
            {
                // 设置请求IP地址、Wap名称、WapUrl
                payParam.ServiceIpAddress = "";
                payParam.WapName = "";
                payParam.WapUrl = "";

                result = await this._wechatManager.WapPay(payParam);
                if (!string.IsNullOrWhiteSpace(input.RedirectUrl))
                {
                    // 拼接支付成功后跳转地址
                    result = $"{result}&redirect_url={HttpUtility.UrlEncode(input.RedirectUrl)}";
                }
            }

            // PC支付
            if (!input.IsMobilePay && !input.IsWechatBrowser)
            {
                result = await this._wechatManager.PcPay(payParam);
            }

            return result;
        }

        /// <summary>
        /// 完成支付，支付宝支付后的回调
        /// </summary>
        /// <param name="receive">支付宝支付回调参数</param>
        /// <returns></returns>
        public async Task<bool> FinishPay(AlipayPayReceive receive)
        {
            if (receive.IsSuccess)
            {
                return await FinishPay(receive.OutTradeNo, receive.TradeNo);
            }

            return false;
        }

        /// <summary>
        /// 完成支付，微信支付后的回调
        /// </summary>
        /// <param name="receive">微信支付回调参数</param>
        /// <returns></returns>
        public async Task<bool> WechatFinishPay(WechatPayReceive receive)
        {
            if (receive.IsSuccess)
            {
                return await FinishPay(receive.OutTradeNo, receive.TradeNo);
            }

            return false;
        }

        /// <summary>
        /// 完成支付
        /// </summary>
        /// <param name="payOrderNo">支付订单号</param>
        /// <param name="tradeNo">支付宝交易号</param>
        /// <returns></returns>
        private async Task<bool> FinishPay(string payOrderNo, string tradeNo = "")
        {
            /* 
             * 1. 先记录日志
             * 2. 判断订单号是否存在并更新订单状态
             * 3. 插入支付日志表
            */

            var payOrder = this._payOrderRepository.FirstOrDefault(x => x.PayOrderNo == payOrderNo);
            if (payOrder == null)
            {
                return false;
            }

            var order = this._orderRepository.GetOrder(payOrder.OrderNo);
            if (order == null)
            {
                return false;
            }

            if (order.OrderType != "5")
            {
                return false;
            }

            if (order.OrderStatus.HasValue && order.OrderStatus != (int)OrderStatus.Normal)
            {
                return false;
            }

            order.OrderStatus = (int)OrderStatus.Payed;
            await this._orderRepository.UpdateOrderStatus(order);

            payOrder.Status = (int)PayStatus.Success;
            if (!string.IsNullOrWhiteSpace(tradeNo))
            {
                payOrder.OutOrderNo = tradeNo;
            }
            this._payOrderRepository.Update(payOrder);

            return true;
        }
    }
}
